﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Drawing;
using System.Drawing.Imaging;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Web;
using DotNetNuke.Common;
using DotNetNuke.Common.Utilities;
using DotNetNuke.Entities.Modules;
using DotNetNuke.Entities.Tabs;
using Encoder=System.Drawing.Imaging.Encoder;

namespace HeroldIT.Dnn.Modules.News
{
    public class NewsController
    {
        #region static fields

        private static readonly ModuleController moduleController = new ModuleController();

        #endregion

        #region static properties

        internal static bool AreSkinObjectsInstalled
        {
            get { return Modules.SettingsHelper.AreSkinObjectsInstalled; }
        }

        #endregion

        #region static methods

        private static string CombineUrls(string url1, string url2)
        {
            StringBuilder sb = new StringBuilder(url1.Length + url2.Length + 1);
            sb.Append(url1);
            if (url1.EndsWith("/", StringComparison.Ordinal))
            {
                sb.Append(url2.StartsWith("/") ? url2.Substring(1) : url2);
            }
            else
            {
                if (!url2.StartsWith("/"))
                {
                    sb.Append("/");
                }
                sb.Append(url2);
            }
            return sb.ToString();
        }

        private static void ResizeImage(string path, int maxWidth, int maxHeight, Color matte, long quality)
        {
            if (!File.Exists(path))
                return;

            using (Image image = ImageHelper.FromFile(path))
            {
                if ((maxWidth > 0 && image.Width > maxWidth) || (maxHeight > 0 && image.Height > maxHeight))
                {
                    quality = Math.Max(Math.Min(quality, 100), 1);

                    ImageCodecInfo jpegCodec =
                        ImageCodecInfo.GetImageEncoders().Where(ie => ie.FormatID.Equals(ImageFormat.Jpeg.Guid)).First();
                    EncoderParameters eps = new EncoderParameters(1) { Param = new[] { new EncoderParameter(Encoder.Quality, quality) } };
                    image.GetProportionallyScaledImage(maxWidth > 0 ? maxWidth : int.MaxValue,
                                                       maxHeight > 0 ? maxHeight : int.MaxValue, matte)
                        .Save(path, jpegCodec, eps);
                }
            }
        }

        #endregion

        #region fields

        private IList<string> categories;

        #endregion

        #region properties

        internal IModuleSettingsProvider Module { get; set; }

        private INewsCoreSettingsProvider newsCore;

        internal INewsCoreSettingsProvider NewsCore
        {
            get
            {
                if (null == this.newsCore)
                {
                    this.newsCore = this.Module as INewsCoreSettingsProvider;

                    if (null == this.newsCore)
                    {
                        INewsSatelliteModuleSettingsProvider satelliteModuleSettingsProvider =
                            this.Module as INewsSatelliteModuleSettingsProvider;
                        if (null != satelliteModuleSettingsProvider)
                        {
                            int id = satelliteModuleSettingsProvider.GetNewsCoreModuleId();
                            // Get specified module
                            if (id >= 0)
                            {
                                ModuleInfo module =
                                    moduleController.GetModuleTabs(id).Cast<ModuleInfo>().FirstOrDefault();
                                if (null != module)
                                {
                                    this.newsCore = new NewsCoreSettingsProvider(module);
                                    return this.newsCore;
                                }
                            }
                            // Try to find the next NewsCore module by iterating all modules on
                            // the active tab and then moving up the tab hierarchy.
                            TabController tc = new TabController();
                            TabInfo tab = tc.GetTab(this.Module.ModuleConfiguration.TabID,
                                                    this.Module.ModuleConfiguration.PortalID, false);
                            do
                            {
                                Dictionary<int, ModuleInfo> tabModules = moduleController.GetTabModules(tab.TabID);
                                foreach (ModuleInfo module in tabModules.Values)
                                {
                                    if (module.ModuleName.Equals("HeroldIT.NewsCore"))
                                    {
                                        this.newsCore = new NewsCoreSettingsProvider(module);
                                        break;
                                    }
                                }
                                if (null != this.newsCore)
                                    break;
                                tab = tab.ParentId > 0 ? tc.GetTab(tab.ParentId, tab.PortalID, false) : null;
                            } while (null != tab);
                        }
                    }
                }
                return this.newsCore;
            }
        }

        internal string NewsCoreId
        {
            get { return this.NewsCore.ModuleConfiguration.ModuleID.ToString(CultureInfo.InvariantCulture); }
        }

        internal int ModuleId
        {
            get
            {
                this.AssertModule();

                return this.Module.ModuleConfiguration.ModuleID;
            }
        }

        #endregion

        #region contructors

        public NewsController()
        {
        }

        public NewsController(IModuleSettingsProvider module)
        {
            this.Module = module;
        }

        #endregion

        #region methods

        #region data methods

        internal int StoreNewsEntry(NewsEntry newsEntry)
        {
            this.AssertCoreModule();
            return DataProvider.Instance.StoreNewsEntry(this.NewsCore.ModuleConfiguration.ModuleID, newsEntry);
        }

        internal void DeleteNewsEntry(int newsEntryId)
        {
            this.AssertCoreModule();
            DataProvider.Instance.DeleteNewsEntry(this.NewsCore.ModuleConfiguration.ModuleID, newsEntryId);
        }

        internal NewsEntry ReadNewsEntry(int newsEntryId)
        {
            this.AssertCoreModule();
            return DataProvider.Instance.ReadNewsEntry(this.NewsCore.ModuleConfiguration.ModuleID, newsEntryId);
        }

        internal NewsEntry ReadNewsEntry(int newsEntryId, string locale)
        {
            this.AssertCoreModule();
            return DataProvider.Instance.ReadNewsEntry(
                this.NewsCore.ModuleConfiguration.ModuleID,
                newsEntryId,
                locale,
                !this.NewsCore.GetDefaultShowOnlyNewsForSelectedLocale()
                    ? this.NewsCore.GetDefaultLocale()
                    : null);
        }

        internal PagedList<NewsEntry> ReadNewsEntries(string locale, int skip, int take)
        {
            this.AssertCoreModule();
            INewsListSettingsProvider listModule = this.Module as INewsListSettingsProvider;
            return DataProvider.Instance.ReadNewsEntries(
                this.NewsCore.ModuleConfiguration.ModuleID,
                null != listModule ? listModule.GetCategoryFilter() : null,
                locale,
                (null == listModule && !this.NewsCore.GetDefaultShowOnlyNewsForSelectedLocale()) || (null != listModule && !listModule.GetShowOnlyNewsForSelectedLocale())
                           ? this.NewsCore.GetDefaultLocale()
                           : null,
                skip,
                take);
        }

        internal PagedList<NewsEntry> ReadNewsEntries(string locale, int skip, int take, bool allEntries)
        {
            this.AssertCoreModule();
            INewsListSettingsProvider listModule = this.Module as INewsListSettingsProvider;
            return DataProvider.Instance.ReadNewsEntries(
                this.NewsCore.ModuleConfiguration.ModuleID,
                null != listModule ? listModule.GetCategoryFilter() : null,
                locale,
                allEntries || (null == listModule && !this.NewsCore.GetDefaultShowOnlyNewsForSelectedLocale()) || (null != listModule && !listModule.GetShowOnlyNewsForSelectedLocale())
                    ? this.NewsCore.GetDefaultLocale()
                    : null,
                skip,
                take,
                allEntries);
        }

        internal PagedList<NewsEntry> FindNewsEntries(string filterText, string locale, int skip, int take)
        {
            this.AssertCoreModule();
            INewsListSettingsProvider listModule = this.Module as INewsListSettingsProvider;
            return DataProvider.Instance.FindNewsEntries(
                this.NewsCore.ModuleConfiguration.ModuleID,
                null != listModule ? listModule.GetCategoryFilter() : null,
                filterText,
                locale,
                (null == listModule && !this.NewsCore.GetDefaultShowOnlyNewsForSelectedLocale()) || (null != listModule && !listModule.GetShowOnlyNewsForSelectedLocale())
                           ? this.NewsCore.GetDefaultLocale()
                           : null,
                skip,
                take);
        }

        internal PagedList<NewsEntry> FindNewsEntries(string filterText, string locale, int skip, int take,
                                                      bool allEntries)
        {
            this.AssertCoreModule();
            INewsListSettingsProvider listModule = this.Module as INewsListSettingsProvider;
            return DataProvider.Instance.FindNewsEntries(
                this.NewsCore.ModuleConfiguration.ModuleID,
                null != listModule ? listModule.GetCategoryFilter() : null,
                filterText,
                locale,
                allEntries || (null == listModule && !this.NewsCore.GetDefaultShowOnlyNewsForSelectedLocale()) || (null != listModule && !listModule.GetShowOnlyNewsForSelectedLocale())
                    ? this.NewsCore.GetDefaultLocale()
                    : null,
                skip,
                take,
                allEntries);
        }

        internal PagedList<NewsEntry> FindNewsEntries(string filterText, string locale, DateTime? from, DateTime? until,
                                                      int skip, int take, bool allEntries)
        {
            this.AssertCoreModule();
            INewsListSettingsProvider listModule = this.Module as INewsListSettingsProvider;
            return DataProvider.Instance.FindNewsEntries(
                this.NewsCore.ModuleConfiguration.ModuleID,
                null != listModule ? listModule.GetCategoryFilter() : null,
                filterText,
                from,
                until,
                locale,
                allEntries || (null == listModule && !this.NewsCore.GetDefaultShowOnlyNewsForSelectedLocale()) || (null != listModule && !listModule.GetShowOnlyNewsForSelectedLocale())
                    ? this.NewsCore.GetDefaultLocale()
                    : null,
                skip,
                take,
                allEntries);
        }

        internal IList<string> GetCategories()
        {
            this.AssertCoreModule();
            if (null == this.categories)
            {
                this.categories = DataProvider.Instance.ReadCategories(this.NewsCore.ModuleConfiguration.ModuleID).ToList();
            }
            return new ReadOnlyCollection<string>(this.categories);
        }

        internal IList<string> GetLocales()
        {
            this.AssertCoreModule();

            var locales = DataProvider.Instance.ReadLocales(this.NewsCore.ModuleConfiguration.ModuleID).ToList();

            return new ReadOnlyCollection<string>(locales);
        }

        #endregion

        #region navigation methods

        internal bool IsDetailView(HttpRequest request)
        {
            int newsEntryId = this.ResolveNewsEntryId(request);
            return (newsEntryId > 0 && null != this.ReadNewsEntry(newsEntryId));
        }

        internal int ResolveNewsEntryId(HttpRequest request)
        {
            this.AssertCoreModule();

            if (null == request)
                request = HttpContext.Current.Request;

            string idValue = request.QueryString["sni[" + this.NewsCoreId + "]"];
            if (null != idValue)
            {
                int id;
                if (int.TryParse(idValue, NumberStyles.Integer, CultureInfo.InvariantCulture, out id))
                {
                    if (id > 0)
                        return id;
                }
            }
            return -1;
        }

        internal string GenerateDetailUrl(int newsEntryId)
        {
            return this.GenerateDetailUrl(-1, newsEntryId);
        }

        internal string GenerateDetailUrl(int tabId, int newsEntryId)
        {
            this.AssertCoreModule();
            INewsListSettingsProvider listModule = this.Module as INewsListSettingsProvider;
            if (tabId < 0 && null != listModule)
                tabId = listModule.GetDetailTabId();
            if (tabId < 0)
                tabId = this.NewsCore.GetDefaultDetailTabId();
            if (tabId <= 0)
                tabId = Null.NullInteger;

            return Globals.NavigateURL(tabId, "",
                                       String.Format(CultureInfo.InvariantCulture, "sni[{0}]={1}",
                                                     this.NewsCoreId, newsEntryId));
        }

        internal string GenerateListUrl()
        {
            this.AssertCoreModule();
            INewsDetailSettingsProvider detailModule = this.Module as INewsDetailSettingsProvider;

            int tabId = -1;
            if (null != detailModule)
                tabId = detailModule.GetListTabId();
            if (tabId < 0)
                tabId = this.NewsCore.GetDefaultListTabId();
            if (tabId <= 0)
                tabId = Null.NullInteger;

            return Globals.NavigateURL(tabId);
        }

        #endregion

        #region image manipulation

        internal void ResizeTeaserImages()
        {
            this.AssertCoreModule();

            foreach (string imageName in DataProvider.Instance.ReadTeaserImageNames(this.NewsCore.ModuleConfiguration.ModuleID))
            {
                this.ResizeTeaserImage(imageName);
            }
        }

        internal void ResizeDetailImages()
        {
            this.AssertCoreModule();

            foreach (string imageName in DataProvider.Instance.ReadDetailImageNames(this.NewsCore.ModuleConfiguration.ModuleID))
            {
                this.ResizeDetailImage(imageName);
            }
        }

        internal void ResizeTeaserImage(string imageName)
        {
            this.AssertCoreModule();

            HttpServerUtility server = HttpContext.Current.Server;
            ResizeImage(server.MapPath(imageName),
                        this.NewsCore.GetMaxTeaserImageWidth(), this.NewsCore.GetMaxTeaserImageHeight(),
                        this.NewsCore.GetTeaserImageMatteColor(), this.NewsCore.GetTeaserImageQuality());
        }

        internal void ResizeDetailImage(string imageName)
        {
            this.AssertCoreModule();

            HttpServerUtility server = HttpContext.Current.Server;
            ResizeImage(server.MapPath(imageName),
                        this.NewsCore.GetMaxDetailImageWidth(), this.NewsCore.GetMaxDetailImageHeight(),
                        this.NewsCore.GetDetailImageMatteColor(), this.NewsCore.GetDetailImageQuality());
        }

        #endregion

        #region settings

        internal int GetMaxCurrentItems()
        {
            this.AssertCoreModule();

            return this.NewsCore.GetMaxCurrentItems();
        }

        internal int GetMaxTeaserImageWidth()
        {
            this.AssertCoreModule();

            return this.NewsCore.GetMaxTeaserImageWidth();
        }

        internal int GetMaxTeaserImageHeight()
        {
            this.AssertCoreModule();

            return this.NewsCore.GetMaxTeaserImageHeight();
        }

        internal long GetTeaserImageQuality()
        {
            this.AssertCoreModule();

            return this.NewsCore.GetTeaserImageQuality();
        }

        internal string GetTeaserImageMatte()
        {
            this.AssertCoreModule();

            return this.NewsCore.GetTeaserImageMatte();
        }

        internal string GetTeaserImageFolder()
        {
            this.AssertCoreModule();

            return this.NewsCore.GetTeaserImageFolder();
        }

        internal int GetMaxDetailImageWidth()
        {
            this.AssertCoreModule();

            return this.NewsCore.GetMaxDetailImageWidth();
        }

        internal int GetMaxDetailImageHeight()
        {
            this.AssertCoreModule();

            return this.NewsCore.GetMaxDetailImageHeight();
        }

        internal long GetDetailImageQuality()
        {
            this.AssertCoreModule();

            return this.NewsCore.GetDetailImageQuality();
        }

        internal string GetDetailImageMatte()
        {
            this.AssertCoreModule();

            return this.NewsCore.GetDetailImageMatte();
        }

        internal string GetDetailImageFolder()
        {
            this.AssertCoreModule();

            return this.NewsCore.GetDetailImageFolder();
        }

        internal string GetDefaultLocale()
        {
            this.AssertCoreModule();

            return this.NewsCore.GetDefaultLocale();
        }

        internal bool GetUseDetailImageForTeaser()
        {
            this.AssertCoreModule();

            return this.NewsCore.GetUseDetailImageForTeaserDefault();
        }

        internal bool GetShowDisabledLanguages()
        {
            this.AssertCoreModule();

            return this.NewsCore.GetShowDisabledLanguages();
        }

        internal int GetMaxImagesToUse()
        {
            this.AssertCoreModule();

            return this.NewsCore.GetMaxImagesToUse();
        }

        #endregion

        #region utility methods

        internal string MakeFolderUrl(string url)
        {
            this.AssertCoreModule();
            url = url.Replace("[I]", this.NewsCore.ModuleConfiguration.ModuleID.ToString(CultureInfo.InvariantCulture));
            return url;
        }

        private void AssertModule()
        {
            if (null == this.Module)
                throw new InvalidOperationException("No module specified.");
        }

        internal bool HasCoreModule()
        {
            return null != this.NewsCore;
        }

        private void AssertCoreModule()
        {
            this.AssertModule();
            if (null == this.NewsCore)
                throw new InvalidOperationException("No news core module specified or found.");
        }

        #endregion

        #endregion
    }
}